using System;
using System.Reflection;

namespace MetaObjects.Core
{
	public static class PropertyExtensions
	{
		public static object GetPropertyReflection(this object container, string propertyName)
		{
			return container.GetType().GetPropertyReflection(container, propertyName);
		}

		public static TValue GetPropertyReflection<TValue>(this object container, string propertyName)
		{
			return (TValue)container.GetType().GetPropertyReflection(container, propertyName);
		}

		public static TValue GetPropertyReflection<T, TValue>(this T container, string propertyName)
		{
			return (TValue)typeof(T).GetPropertyReflection(container, propertyName);
		}

		public static object GetPropertyReflection<T>(this T container, string propertyName)
		{
			return typeof(T).GetPropertyReflection(container, propertyName);
		}

		public static object GetPropertyReflection(this Type type, object container, string propertyName)
		{
			return type.GetProperty(propertyName, BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).GetValue(container, new object[0]);
		}

		public static void SetPropertyReflection(this object container, string propertyName, object value)
		{
			container.GetType().GetProperty(propertyName, BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).SetValue(container, value, new object[0]);
		}

		public static void SetPropertyReflection<T>(this T container, string propertyName, object value)
		{
			typeof(T).GetProperty(propertyName, BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).SetValue(container, value, new object[0]);
		}
		
		public static object GetPropertyValue<T>(this T container, string propertyName)
		{
			return PropertyAccessor<T>.GetPropertyDelegate(propertyName)(container);
		}

		public static TValue GetPropertyValue<T, TValue>(this T container, string propertyName)
		{
			return PropertyAccessor<T>.GetPropertyDelegate<TValue>(propertyName)(container);
		}

		public static void SetPropertyValue<T>(this T container, string propertyName, object value)
		{
			PropertyAccessor<T>.SetPropertyDelegate(propertyName)(container, value);
		}

		public static void SetPropertyValue<T, TValue>(this T container, string propertyName, TValue value)
		{
			PropertyAccessor<T>.SetPropertyDelegate<TValue>(propertyName)(container, value);
		}

		public static Func<T1, TResult> Compose<T1, T2, TResult>(this Func<T1, T2> func1, Func<T2, TResult> func2)
		{
			return t1 => func2(func1(t1));
		}
	}
}